package com.credithc.customers.manager.asnycTask;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.credithc.baseserv.core.utils.UUIDUtils;
import com.credithc.customers.base.codes.CustomerCodes;
import com.credithc.customers.base.constants.CustomerConstants;
import com.credithc.customers.base.exception.CustomerException;
import com.credithc.customers.base.mapper.CustomerImportMapper;
import com.credithc.customers.base.mapper.PreCustomerMapper;
import com.credithc.customers.base.po.*;
import com.credithc.customers.base.ro.*;
import com.credithc.customers.base.service.IBaseCustomerService;
import com.credithc.customers.base.service.IBasePreCustomerService;
import com.credithc.customers.base.utils.CustomerUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

import java.io.*;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author zhukai
 */
@Component
@Slf4j
public class AsyncTask {

    private static final Integer PRE_CELL_NUMBER = 5;
    private static final Integer PERSONAL_CELL_NUMBER = 8;
    private static final Integer COMPANY_CELL_NUMBER = 14;
    private static final String ENTER = "\n";
    private static final String SUCCESS = "SUCCESS";
    private static final String FAIL = "FAIL";
    private static final String ALL = "ALL";

    private static final Map<String, Integer> COUNT_MAP = new HashMap<>();

    private static int threadNumber = 10;

    private final IBasePreCustomerService basePreCustomerService;

    private final IBaseCustomerService baseCustomerService;

    private final CustomerImportMapper importMapper;

    private final PreCustomerMapper preCustomerMapper;

    private static int getOrSetVar(String type, String importId, int plus) {
        synchronized (AsyncTask.COUNT_MAP) {
            Integer integer = AsyncTask.COUNT_MAP.get(type + "-" + importId);
            if (integer == null) {
                integer = 0;
            }
            if (plus != 0) {
                integer = integer + plus;
                AsyncTask.COUNT_MAP.put(type + "-" + importId, integer);
            }
            return integer;
        }
    }

    @Autowired
    public AsyncTask(IBasePreCustomerService basePreCustomerService, CustomerImportMapper importMapper, IBaseCustomerService baseCustomerService, PreCustomerMapper preCustomerMapper) {
        this.basePreCustomerService = basePreCustomerService;
        this.importMapper = importMapper;
        this.baseCustomerService = baseCustomerService;
        this.preCustomerMapper = preCustomerMapper;
    }


    @Async
    public Future<Boolean> uploadFile(String path, String importId, Integer importType) {
        AsyncTask.getOrSetVar(SUCCESS, importId, 0);
        AsyncTask.getOrSetVar(FAIL, importId, 0);
        AsyncTask.getOrSetVar(ALL, importId, 0);

        long start = System.currentTimeMillis();
        try {
            File file = new File(path);
            if (file.exists()) {
                FileWriter successFw;
                BufferedWriter successBw;
                FileWriter failFw;
                BufferedWriter failBw;

                log.info("====================开始将文件缓存到内存");
                long fileLength = file.length();
                byte[] fileContent = new byte[(int) fileLength];
                FileInputStream in = new FileInputStream(file);
                in.read(fileContent);
                in.close();
                log.info("====================文件缓存结束");

                //将文件内容转成list
                String[] rows = new String(fileContent).split(ENTER);
                List<String> sourceList = Arrays.asList(rows);
                int sourceSize = sourceList.size();
                log.info("原始数据量==========================" + sourceSize + "条");
                Stream<String> distinct = sourceList.stream().distinct();
                List<String> retList = distinct.collect(Collectors.toList());
                int retSize = retList.size();
                log.info("去重之后数据量==========================" + retSize + "条");
                log.info("重复数据量==========================" + (retSize - sourceSize) + "条");

                //开始处理逻辑
                if (retSize > 0) {
                    //创建要下载的文件
                    //成功的数据
                    String successPath = path.replace("-upload.", "-success.");
                    File successFile = new File(successPath);
                    if (!successFile.exists()) {
                        successFile.createNewFile();
                    }
                    //失败的数据
                    String failPath = path.replace("-upload.", "-fail.");
                    File failFile = new File(failPath);
                    if (!failFile.exists()) {
                        failFile.createNewFile();
                    }
                    successFw = new FileWriter(successFile);
                    successBw = new BufferedWriter(successFw);
                    failFw = new FileWriter(failFile);
                    failBw = new BufferedWriter(failFw);
                    startThread(retList, importId, successBw, failBw, importType);
                    successBw.flush();
                    failBw.flush();
                    successBw.close();
                    failBw.close();
                }

                long end = System.currentTimeMillis();
                //更新import主表
                CustomerImportPo importPo = importMapper.selectByPrimaryKey(importId);
                importPo.setCount(AsyncTask.getOrSetVar(ALL, importId, 0));
                importPo.setFailCount(AsyncTask.getOrSetVar(FAIL, importId, 0));
                importPo.setSuccessCount(AsyncTask.getOrSetVar(SUCCESS, importId, 0));
                importPo.setFinishTime(new Timestamp(System.currentTimeMillis()));
                importPo.setImportStatus(CustomerConstants.IMPORT_STATUS_SUCCESS);
                importMapper.updateByPrimaryKeySelective(importPo);
                log.info("一共耗时==================================" + (end - start) + "ms");
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new AsyncResult<>(true);
    }


    private void startThread(List<String> retList, String importId, BufferedWriter successBw, BufferedWriter failBw, Integer importType) {
        try {
            //初始线程数
            int retSize = retList.size();
            //启动多线程
            if (threadNumber > retSize) {
                threadNumber = retSize;
            }
            int baseNum = retSize / threadNumber;
            int remainderNum = retSize % threadNumber;
            int end = 0;
            final CountDownLatch countDownLatch = new CountDownLatch(threadNumber);
            for (int i = 0; i < threadNumber; i++) {
                int start = end;
                end = start + baseNum;
                if (i == (threadNumber - 1)) {
                    end = retSize;
                } else if (i < remainderNum) {
                    end = end + 1;
                }

                BThread bThread = new BThread("线程[" + (i + 1) + "]", retList, start, end, importId, countDownLatch, successBw, failBw, importType);
                bThread.start();
            }
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    class BThread extends Thread {

        private String threadName;
        private List<String> list;
        private int startIndex;
        private int endIndex;
        private String importId;
        private CountDownLatch countDownLatch;
        private BufferedWriter successBw;
        private BufferedWriter failBw;
        private Integer importType;


        BThread(String threadName, List<String> list, int startIndex, int endIndex, String importId, CountDownLatch countDownLatch, BufferedWriter successBw, BufferedWriter failBw, Integer importType) {
            this.threadName = threadName;
            this.list = list;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.importId = importId;
            this.countDownLatch = countDownLatch;
            this.successBw = successBw;
            this.failBw = failBw;
            this.importType = importType;
        }

        @Override
        public void run() {
            List<String> subList = list.subList(startIndex, endIndex);
            if (importType.equals(CustomerConstants.IMPORT_TYPE_PRE)) {
                importPre(subList, successBw, failBw, importId);
            } else if (importType.equals(CustomerConstants.IMPORT_TYPE_PERSONAL)) {
                importPersonal(subList, successBw, failBw, importId);
            } else if (importType.equals(CustomerConstants.IMPORT_TYPE_COMPANY)) {
                importCompany(subList, successBw, failBw, importId);
            }
            countDownLatch.countDown();
        }

        private void importPre(List<String> retList, BufferedWriter successBw, BufferedWriter failBw, String importId) {
            try {
                String line;
                for (int i = 0; i < retList.size(); i++) {
                    log.info("正在处理=====================================" + i + "行");
                    line = retList.get(i);
                    AsyncTask.getOrSetVar(ALL, importId, 1);
                    CustomerImportPrePo prePo = new CustomerImportPrePo();
                    prePo.setImportId(importId);
                    prePo.setCreateTime(new Timestamp(System.currentTimeMillis()));
                    String[] rowArr = line.split(";");
                    prePo.setUploadData(line);
                    if (rowArr.length != PRE_CELL_NUMBER) {
                        AsyncTask.getOrSetVar(FAIL, importId, 1);
                        prePo.setStatus(CustomerConstants.STATUS_FAIL);
                        prePo.setRemark("所有项必填");
                        synchronized (failBw) {
                            failBw.write(line + ";" + "所有项必填");
                            failBw.write(ENTER);
                        }
                    } else {
                        try {
                            CustomerUtils.checkContact(rowArr[2], rowArr[3]);
                            BaseRequest<PreCustomerRo> request = new BaseRequest<>();
                            request.setSystemSign(rowArr[0]);
                            request.setSubSystemSign(rowArr[1]);
                            request.setRequestTimestamp(System.currentTimeMillis());
                            PreCustomerRo requestRo = new PreCustomerRo();
                            requestRo.setContactType(rowArr[2]);
                            requestRo.setContactInfo(rowArr[3]);
                            requestRo.setContactVerified(false);
                            requestRo.setRequestNo("IMPORT_PRE_" + UUIDUtils.getUUID());
                            requestRo.setRemark("存量数据导入");
                            request.setBody(requestRo);
                            Map<String, String> map = basePreCustomerService.createPreCustomerNo4Import(request);
                            if (map != null && map.containsKey("preCustomerNo")) {
                                AsyncTask.getOrSetVar(SUCCESS, importId, 1);
                                prePo.setStatus(CustomerConstants.STATUS_SUCCESS);
                                String preCustomerNo = map.get("preCustomerNo");
                                prePo.setCustomerNo(preCustomerNo);
                                synchronized (successBw) {
                                    successBw.write(line + ";" + preCustomerNo);
                                    successBw.write(ENTER);
                                }
                            }
                        } catch (CustomerException e) {
                            log.error("============================" + e.getMessage());
                            AsyncTask.getOrSetVar(FAIL, importId, 1);
                            prePo.setStatus(CustomerConstants.STATUS_FAIL);
                            prePo.setRemark(e.getMsg());
                            synchronized (failBw) {
                                failBw.write(line + ";" + e.getMsg());
                                failBw.write(ENTER);
                            }
                        } catch (Exception e) {
                            log.error("============================" + e.getMessage());
                            AsyncTask.getOrSetVar(FAIL, importId, 1);
                            prePo.setStatus(CustomerConstants.STATUS_FAIL);
                            prePo.setRemark("未知原因");
                            synchronized (failBw) {
                                failBw.write(line + ";" + "未知原因");
                                failBw.write(ENTER);
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        private void importPersonal(List<String> retList, BufferedWriter successBw, BufferedWriter failBw, String importId) {
            try {
                String line;
                for (int i = 0; i < retList.size(); i++) {
                    log.info("正在处理=====================================" + i + "行");
                    line = retList.get(i);
                    AsyncTask.getOrSetVar(ALL, importId, 1);
                    CustomerImportPersonalPo personalPo = new CustomerImportPersonalPo();
                    personalPo.setImportId(importId);
                    personalPo.setCreateTime(new Timestamp(System.currentTimeMillis()));
                    String[] rowArr = line.split(";");
                    personalPo.setUploadData(line);
                    if (rowArr.length != PERSONAL_CELL_NUMBER) {
                        AsyncTask.getOrSetVar(FAIL, importId, 1);
                        personalPo.setStatus(CustomerConstants.STATUS_FAIL);
                        personalPo.setRemark("数据格式不对");
                        synchronized (failBw) {
                            failBw.write(line + ";" + "数据格式不对");
                            failBw.write(ENTER);
                        }
                    } else {
                        try {
                            String elementsVerified = rowArr[5];
                            if (!"true".equals(elementsVerified) && !"false".equals(elementsVerified)) {
                                AsyncTask.getOrSetVar(FAIL, importId, 1);
                                personalPo.setStatus(CustomerConstants.STATUS_FAIL);
                                personalPo.setRemark("三要素是否已核验数据格式不正确");
                                synchronized (failBw) {
                                    failBw.write(line + ";" + "三要素是否已核验数据格式不正确");
                                    failBw.write(ENTER);
                                }
                            } else {
                                List<ContactRo> contacts = generateContacts(rowArr[6]);
                                BaseRequest<CustomerInfoRo> request = new BaseRequest<>();
                                request.setSystemSign(rowArr[0]);
                                request.setSubSystemSign(rowArr[1]);
                                request.setRequestTimestamp(System.currentTimeMillis());
                                CustomerInfoRo requestRo = new CustomerInfoRo();
                                requestRo.setRequestNo("IMPORT_PERSONAL_" + UUIDUtils.getUUID());
                                requestRo.setCustomerType(CustomerConstants.CUSTOMER_TYPE_PERSON);
                                requestRo.setRemark("存量数据导入");
                                CustomerIdInfoRo idInfo = new CustomerIdInfoRo(rowArr[3], rowArr[4], rowArr[2], "true".equals(elementsVerified));
                                requestRo.setIdInfo(idInfo);
                                requestRo.setContacts(contacts);
                                String preCustomerNo = getPreCustomerNo(contacts);
                                if(StringUtils.isNotBlank(preCustomerNo)){
                                    request.getBody().setPreCustomerNo(preCustomerNo);
                                }
                                request.setBody(requestRo);
                                Map<String, String> map = baseCustomerService.createCustomerNo4Import(request);
                                if (map != null && map.containsKey("customerNo")) {
                                    AsyncTask.getOrSetVar(SUCCESS, importId, 1);
                                    personalPo.setStatus(CustomerConstants.STATUS_SUCCESS);
                                    String customerNo = map.get("customerNo");
                                    personalPo.setCustomerNo(customerNo);
                                    synchronized (successBw) {
                                        successBw.write(line + ";" + customerNo);
                                        successBw.write(ENTER);
                                    }
                                    personalPo.setCustomerNo(map.get("customerNo"));
                                }
                            }
                        } catch (CustomerException e) {
                            log.error("=======================" + e.getMessage());
                            AsyncTask.getOrSetVar(FAIL, importId, 1);
                            personalPo.setStatus(CustomerConstants.STATUS_FAIL);
                            personalPo.setRemark(e.getMsg());
                            synchronized (failBw) {
                                failBw.write(line + ";" + e.getMsg());
                                failBw.write(ENTER);
                            }
                        } catch (Exception e) {
                            log.error("=======================" + e.getMessage());
                            AsyncTask.getOrSetVar(FAIL, importId, 1);
                            personalPo.setStatus(CustomerConstants.STATUS_FAIL);
                            personalPo.setRemark("未知原因");
                            synchronized (failBw) {
                                failBw.write(line + ";" + "未知原因");
                                failBw.write(ENTER);
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        private void importCompany(List<String> retList, BufferedWriter successBw, BufferedWriter failBw, String importId) {
            try {
                String line;
                for (int i = 0; i < retList.size(); i++) {
                    line = retList.get(i);
                    AsyncTask.getOrSetVar(ALL, importId, 1);
                    CustomerImportCompanyPo companyPo = new CustomerImportCompanyPo();
                    companyPo.setImportId(importId);
                    companyPo.setCreateTime(new Timestamp(System.currentTimeMillis()));
                    String[] rowArr = line.split(";");
                    companyPo.setUploadData(line);
                    if (rowArr.length != COMPANY_CELL_NUMBER) {
                        AsyncTask.getOrSetVar(FAIL, importId, 1);
                        companyPo.setStatus(CustomerConstants.STATUS_FAIL);
                        companyPo.setRemark("数据格式不对");
                        synchronized (failBw) {
                            failBw.write(line + ";" + "数据格式不对");
                            failBw.write(ENTER);
                        }
                    } else {
                        try {
                            String elementsVerified = rowArr[11];
                            String customerType = rowArr[2];
                            if (!"true".equals(elementsVerified) && !"false".equals(elementsVerified)) {
                                AsyncTask.getOrSetVar(FAIL, importId, 1);
                                companyPo.setStatus(CustomerConstants.STATUS_FAIL);
                                companyPo.setRemark("三要素是否已核验数据格式不正确");
                                synchronized (failBw) {
                                    failBw.write(line + ";" + "三要素是否已核验数据格式不正确");
                                    failBw.write(ENTER);
                                }
                            } else if (StringUtils.isBlank(customerType) || (!"2".equals(customerType) && !"3".equals(customerType))) {
                                AsyncTask.getOrSetVar(FAIL, importId, 1);
                                companyPo.setStatus(CustomerConstants.STATUS_FAIL);
                                companyPo.setRemark("客户类型不正确");
                                synchronized (failBw) {
                                    failBw.write(line + ";" + "客户类型不正确");
                                    failBw.write(ENTER);
                                }
                            } else {
                                List<ContactRo> contacts = generateContacts(rowArr[12]);
                                BaseRequest<CustomerInfoRo> request = new BaseRequest<>();
                                request.setSystemSign(rowArr[0]);
                                request.setSubSystemSign(rowArr[1]);
                                request.setRequestTimestamp(System.currentTimeMillis());
                                CustomerInfoRo requestRo = new CustomerInfoRo();
                                requestRo.setRequestNo("IMPORT_" + UUIDUtils.getUUID());
                                requestRo.setCustomerType(customerType);
                                requestRo.setRemark("存量数据导入");
                                CompanyIdInfoRo companyIdInfo = new CompanyIdInfoRo();
                                companyIdInfo.setIdCardType(rowArr[4]);
                                companyIdInfo.setIdCardNo(rowArr[5]);
                                companyIdInfo.setCustomerName(rowArr[3]);
                                companyIdInfo.setOrgNo(rowArr[6]);
                                companyIdInfo.setLicenseNo(rowArr[7]);
                                companyIdInfo.setTaxNo(rowArr[8]);
                                companyIdInfo.setUnifiedCode(rowArr[9]);
                                companyIdInfo.setLegalName(rowArr[10]);
                                companyIdInfo.setElementsVerified("true".equals(elementsVerified));
                                requestRo.setCompanyIdInfo(companyIdInfo);
                                requestRo.setContacts(contacts);
                                String preCustomerNo = getPreCustomerNo(contacts);
                                if(StringUtils.isNotBlank(preCustomerNo)){
                                    request.getBody().setPreCustomerNo(preCustomerNo);
                                }
                                request.setBody(requestRo);
                                Map<String, String> map = baseCustomerService.createCustomerNo4Import(request);
                                if (map != null && map.containsKey("customerNo")) {
                                    AsyncTask.getOrSetVar(SUCCESS, importId, 1);
                                    companyPo.setStatus(CustomerConstants.STATUS_SUCCESS);
                                    String customerNo = map.get("customerNo");
                                    companyPo.setCustomerNo(customerNo);
                                    synchronized (successBw) {
                                        successBw.write(line + ";" + customerNo);
                                        successBw.write(ENTER);
                                    }
                                }
                            }
                        } catch (CustomerException e) {
                            AsyncTask.getOrSetVar(FAIL, importId, 1);
                            companyPo.setStatus(CustomerConstants.STATUS_FAIL);
                            companyPo.setRemark(e.getMsg());
                            synchronized (failBw) {
                                failBw.write(line + ";" + e.getMsg());
                                failBw.write(ENTER);
                            }
                        } catch (Exception e) {
                            AsyncTask.getOrSetVar(FAIL, importId, 1);
                            companyPo.setStatus(CustomerConstants.STATUS_FAIL);
                            companyPo.setRemark("未知原因");
                            synchronized (failBw) {
                                failBw.write(line + ";" + "未知原因");
                                failBw.write(ENTER);
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        private List<ContactRo> generateContacts(String contactInfo) {
            List<ContactRo> contacts = new ArrayList<>();
            if (StringUtils.isNotBlank(contactInfo)) {
                try {
                    JSONArray arr = (JSONArray) JSONArray.parse(contactInfo);
                    for (int i = 0; i < arr.size(); i++) {
                        Map<String, String> map = (Map) arr.get(i);
                        for (Map.Entry<String, String> entry : map.entrySet()) {
                            ContactRo contactRo = new ContactRo();
                            contactRo.setContactType(entry.getKey());
                            contactRo.setContactInfo(entry.getValue());
                            contacts.add(contactRo);
                        }
                    }
                } catch (JSONException e) {
                    throw new CustomerException(CustomerCodes.BIZ_VALIDATE_ERROR, "联系方式格式不正确");
                }
            }
            return contacts;
        }

        private String getPreCustomerNo(List<ContactRo> contacts) {
            String contactType = contacts.get(0).getContactType();
            String contactInfo = contacts.get(0).getContactInfo();
            if (StringUtils.isNotBlank(contactType) && StringUtils.isNotBlank(contactInfo)) {
                PreCustomerInfoPo po = new PreCustomerInfoPo();
                po.setContactType(contactType);
                po.setContactInfo(contactInfo);
                PreCustomerInfoPo preCustomerInfoPo = preCustomerMapper.selectOne(po);
                if (preCustomerInfoPo != null) {
                    return po.getPreCustomerNo();
                }
            }
            return null;
        }

    }

}
